﻿/**
 * Copyright (C) 2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Matcher;
using NGuice.Inject.Internal.Util;
using System.Reflection;

namespace NGuice.Inject.Internal
{
    //Complete!
    /// <summary>
    /// 处理Binder.ConverToTypes命令
    /// </summary>
    internal sealed class TypeConverterBindingProcessor : AbstractProcessor
    {
        internal TypeConverterBindingProcessor(Errors errors)
            :base(errors)
        {
        }

        private void ConvertToClass<T>(ITypeConverter converter)
        {
            ConvertToClass(typeof(T), converter);
        }

        /// <summary>
        /// 为原始类型、枚举类型、Type类型安装默认的转换器
        /// </summary>
        /// <param name="injector"></param>
        internal void PrepareBuiltInConverters(InjectorImpl injector)
        {
            this.injector = injector;
            try
            {
                // Configure type converters.
                ConvertToPrimitiveType(typeof(int));
                ConvertToPrimitiveType(typeof(long));
                ConvertToPrimitiveType(typeof(bool));
                ConvertToPrimitiveType(typeof(byte));
                ConvertToPrimitiveType(typeof(short));
                ConvertToPrimitiveType(typeof(float));
                ConvertToPrimitiveType(typeof(double));
                ConvertToClass(typeof(char), new CharTypeConverter());
                ConvertToClasses(Matchers.SubclassesOf(typeof(Enum)), new EnumTypeConverter());
                InternalConvertToTypes(new MemberInfoMatcher(), new ClassConverter());
            }
            finally
            {
                this.injector = null;
            }
        }

        private class CharTypeConverter : ITypeConverter
        {

            public object Convert(string value, Type toType)
            {
                value = value.Trim();
                if (value.Length != 1)
                {
                    throw new ApplicationException("Length != 1.");
                }
                return value[0];
            }

            public override string ToString()
            {
                return "TypeConverter<Character>";
            }
        }

        private class EnumTypeConverter : ITypeConverter
        {
            public object Convert(string value, Type toType)
            {
                return Enum.Parse(toType, value);
            }

            public override string ToString()
            {
                return "TypeConverter<E> where E: Enum<E>";
            }
        }

        private void ConvertToPrimitiveType(Type primitiveType)
        {
            ITypeConverter converter = new ParseMethodPrimitiveTypeConverter(primitiveType);
            ConvertToClass(primitiveType, converter);
        }

        private class ParseMethodPrimitiveTypeConverter : ITypeConverter
        {
            private Type primitiveType;

            private MethodInfo parser;

            public ParseMethodPrimitiveTypeConverter(Type primitiveType)
            {
                if (!primitiveType.IsPrimitive)
                    throw new ArgumentException("the type:" + primitiveType.Name + " is not primitive");
                this.primitiveType = primitiveType;
                try
                {
                    MethodInfo parser = primitiveType.GetMethod("Parse", new Type[] { typeof(string) });
                }
                catch (AmbiguousMatchException e)
                {
                    throw new AssertionError("no parse method", e);
                }
            }

            public object Convert(string value, Type toType)
            {
                try
                {
                    return parser.Invoke(null, new object[] { value });
                }
                catch (MethodAccessException e)
                {
                    throw new AssertionError("no access invoke method", e);
                }
                catch (TargetInvocationException e)
                {
                    throw new ApplicationException(e.InnerException.Message);
                }
            }

            public override string ToString()
            {
                return "TypeConverter<" + primitiveType.Name + ">";
            }
        }

        private void ConvertToClass(Type type, ITypeConverter converter)
        {
            ConvertToClasses(Matchers.IdenticalTo(type), converter);
        }

        private void ConvertToClasses(IMatcher typeMatcher, ITypeConverter converter)
        {
            InternalConvertToTypes(new TypeMatcher(typeMatcher), converter);
        }

        private class TypeMatcher : AbstractMatcher<Type>
        {
            private IMatcher typeMatcher;

            public TypeMatcher(IMatcher typeMatcher)
            {
                Preconditions.CheckSuperGenericParameter<Type>(typeMatcher);
                this.typeMatcher = typeMatcher;
            }

            public override bool Match(Type t)
            {
                return typeMatcher.Matches(t);
            }

            public override string ToString()
            {
                return typeMatcher.ToString();
            }
        }

        private class MemberInfoMatcher : AbstractMatcher<MemberInfo>
        {
            public override bool Match(MemberInfo t)
            {
                return t.GetType() == typeof(Type);
            }

            public override string ToString()
            {
                return "Type";
            }
        }

        private class ClassConverter : ITypeConverter
        {
            public object Convert(string value, Type toType)
            {
                try
                {
                    Type t = Type.GetType(value);
                    if (t == null)
                        throw new ApplicationException("type:" + value + " do not found");
                    return t;
                }
                catch (Exception e)
                {
                    throw new ApplicationException(e.Message);
                }
            }

            public override string ToString()
            {
                return "TypeConverter<Type>";
            }
        }

        private void InternalConvertToTypes(IMatcher typeMatcher, ITypeConverter converter)
        {
            Preconditions.CheckSuperGenericParameter<Type>(typeMatcher);
            injector.state.AddConverter(
                new TypeConverterBinding(SourceProvider.UNKNOWN_SOURCE, typeMatcher, converter));
        }

        public override bool Visit(TypeConverterBinding command)
        {
            injector.state.AddConverter(new TypeConverterBinding(
                command.Source, command.TypeMatcher, command.TypeConverter));
            return true;
        }
    }
}
